/** * Copyright (C) 2005 - 2012 Eric Van Dewoestine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.eclim.plugin.jdt.command.src; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildLogger; import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; import org.apache.tools.ant.taskdefs.Java; import org.apache.tools.ant.taskdefs.PumpStreamHandler; import org.apache.tools.ant.taskdefs.Redirector; import org.apache.tools.ant.types.Commandline.Argument; import org.apache.tools.ant.types.Environment.Variable; import org.apache.tools.ant.types.Path; import org.eclim.Services; import org.eclim.annotation.Command; import org.eclim.command.CommandLine; import org.eclim.command.Options; import org.eclim.plugin.core.command.AbstractCommand; import org.eclim.plugin.core.util.ProjectUtils; import org.eclim.plugin.jdt.util.ClasspathUtils; import org.eclim.plugin.jdt.util.JavaUtils; import org.eclim.util.StringUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import com.google.gson.Gson; /** * Command to run the project's main class. * * @author Eric Van Dewoestine */ @Command( name = "java", options = "REQUIRED p project ARG," + "OPTIONAL d debug NOARG," + "OPTIONAL c classname ARG," + "OPTIONAL w workingdir ARG," + "OPTIONAL v vmargs ANY," + "OPTIONAL s sysprops ANY," + "OPTIONAL e envargs ANY," + "OPTIONAL a args ANY" ) public class JavaCommand extends AbstractCommand { private static final String WORKINGDIR_OPTION = "w"; private static final String VMARGS_OPTION = "v"; private static final String SYSPROPS_OPTION = "s"; private static final String ENVARGS_OPTION = "e"; /** * {@inheritDoc} * @see org.eclim.command.Command#execute(CommandLine) */ public Object execute(CommandLine commandLine) throws Exception { String projectName = commandLine.getValue(Options.PROJECT_OPTION); String mainClass = commandLine.getValue(Options.CLASSNAME_OPTION); boolean debug = commandLine.hasOption(Options.DEBUG_OPTION); String workingDir = commandLine.getValue(WORKINGDIR_OPTION); IProject project = ProjectUtils.getProject(projectName); IJavaProject javaProject = JavaUtils.getJavaProject(project); Project antProject = new Project(); BuildLogger buildLogger = new DefaultLogger(); buildLogger.setMessageOutputLevel(debug ? Project.MSG_DEBUG : Project.MSG_INFO); buildLogger.setOutputPrintStream(getContext().out); buildLogger.setErrorPrintStream(getContext().err); antProject.addBuildListener(buildLogger); antProject.setBasedir(ProjectUtils.getPath(project)); antProject.setDefaultInputStream(System.in); if (mainClass == null){ mainClass = ProjectUtils.getSetting(project, "org.eclim.java.run.mainclass"); } if (mainClass == null || mainClass.trim().equals(StringUtils.EMPTY) || mainClass.trim().equals("none")) { // first try to locate a main method. mainClass = findMainClass(javaProject); if (mainClass == null){ throw new RuntimeException(Services.getMessage( "setting.not.set", "org.eclim.java.run.mainclass")); } } if (mainClass.endsWith(".java") || mainClass.endsWith(".class")){ mainClass = mainClass.substring(0, mainClass.lastIndexOf('.')); } Java java = new MyJava(); java.setTaskName("java"); java.setProject(antProject); java.setClassname(mainClass); java.setFork(true); if (workingDir != null){ java.setDir(new File(workingDir)); } // construct classpath Path classpath = new Path(antProject); String[] paths = ClasspathUtils.getClasspath(javaProject); for (String path : paths){ Path.PathElement pe = classpath.createPathElement(); pe.setPath(path); } java.setClasspath(classpath); // add default vm args String setting = ProjectUtils.getSetting(project, "org.eclim.java.run.jvmargs"); if (setting != null && !setting.trim().equals(StringUtils.EMPTY)){ String[] defaultArgs = (String[])new Gson().fromJson(setting, String[].class); if (defaultArgs != null && defaultArgs.length > 0){ for(String vmarg : defaultArgs){ if (!vmarg.startsWith("-")){ continue; } Argument a = java.createJvmarg(); a.setValue(vmarg); } } } // add any supplied vm args String[] vmargs = commandLine.getValues(VMARGS_OPTION); if (vmargs != null && vmargs.length > 0){ for(String vmarg : vmargs){ if (!vmarg.startsWith("-")){ continue; } Argument a = java.createJvmarg(); a.setValue(vmarg); } } // add any supplied system properties String[] props = commandLine.getValues(SYSPROPS_OPTION); if (props != null && props.length > 0){ for(String prop : props){ String[] sysprop = StringUtils.split(prop, "=", 2); if (sysprop.length != 2){ continue; } if (sysprop[0].startsWith("-D")){ sysprop[0] = sysprop[0].substring(2); } Variable var = new Variable(); var.setKey(sysprop[0]); var.setValue(sysprop[1]); java.addSysproperty(var); } } // add any env vars String[] envs = commandLine.getValues(ENVARGS_OPTION); if (envs != null && envs.length > 0){ for(String env : envs){ String[] envvar = StringUtils.split(env, "=", 2); if (envvar.length != 2){ continue; } Variable var = new Variable(); var.setKey(envvar[0]); var.setValue(envvar[1]); java.addEnv(var); } } // add any supplied command line args String[] args = commandLine.getValues(Options.ARGS_OPTION); if (args != null && args.length > 0){ for(String arg : args){ Argument a = java.createArg(); a.setValue(arg); } } java.execute(); return null; } private String findMainClass(IJavaProject javaProject) throws Exception { final String projectPath = ProjectUtils.getPath(javaProject.getProject()); final ArrayList<IMethod> methods = new ArrayList<IMethod>(); int context = IJavaSearchConstants.DECLARATIONS; int type = IJavaSearchConstants.METHOD; int matchType = SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE; IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[]{javaProject}); SearchPattern pattern = SearchPattern.createPattern("main(String[])", type, context, matchType); SearchRequestor requestor = new SearchRequestor(){ public void acceptSearchMatch(SearchMatch match){ if(match.getAccuracy() != SearchMatch.A_ACCURATE){ return; } IPath location = match.getResource().getLocation(); if (location == null){ return; } String path = location.toOSString().replace('\\', '/'); if (!path.toLowerCase().startsWith(projectPath.toLowerCase())){ return; } IJavaElement element = (IJavaElement)match.getElement(); if (element.getElementType() != IJavaElement.METHOD){ return; } IMethod method = (IMethod)element; String[] params = method.getParameterTypes(); if (params.length != 1){ return; } methods.add(method); } }; if(pattern != null){ SearchEngine engine = new SearchEngine(); SearchParticipant[] participants = new SearchParticipant[]{SearchEngine.getDefaultSearchParticipant()}; engine.search(pattern, participants, scope, requestor, null); // if we found only 1 result, we can use it. if (methods.size() == 1){ IMethod method = methods.get(0); ICompilationUnit cu = method.getCompilationUnit(); IPackageDeclaration[] packages = cu.getPackageDeclarations(); if (packages != null && packages.length > 0){ return packages[0].getElementName() + "." + cu.getElementName(); } return cu.getElementName(); } } return null; } /* All of this is to ensure that System.out calls by the running class are * flushed immediatly to the console for things like user input prompts. */ private class MyJava extends Java { public MyJava() { super(); this.redirector = new MyRedirector(this); } } private class MyRedirector extends Redirector { public MyRedirector(Task task) { super(task); } @Override public synchronized ExecuteStreamHandler createHandler() throws BuildException { return new MyPumpStreamHandler(); } @Override public synchronized void complete() throws IOException { getContext().out.flush(); getContext().err.flush(); } } private class MyPumpStreamHandler extends PumpStreamHandler { public MyPumpStreamHandler() { super(new FlushingOutputStream( getContext().out), getContext().err, getContext().in); } } private class FlushingOutputStream extends OutputStream { private OutputStream out; public FlushingOutputStream(OutputStream out) { this.out = out; } @Override public void write(int b) throws IOException { out.write(b); out.flush(); } @Override public void write(byte[] b) throws IOException { out.write(b); out.flush(); } @Override public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); out.flush(); } } }